﻿using System;
using System.Globalization;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.SdkTypeProxy.Metadata;

namespace CSP.CRM.ISVExtensions
{

  /// <summary>
  /// Used by GetProcedureMetadata for createprocedure.aspx 
  /// Represents a string-int pair of procedure name and integer code
  /// </summary>
  class EntityInfo
  {
    public string EntityName { get { return entityName; } }
    public int EntityTypeCode { get { return entityTypeCode; } }

    private string entityName;
    private int entityTypeCode;

    public EntityInfo(string entityName, int entityTypeCode)
    {
      this.entityName = entityName;
      this.entityTypeCode = entityTypeCode;
    }
  }



  [DataObject]
  public class CspInfo : IDisposable
  {

    private CrmService crm;
    private MetadataService meta;
    private AppSettingsReader config;

    private Uri crmServerUrl;
    private int languageCode;
    private string organizationName;

    public CspInfo(Uri crmServerUrl, string organizationName, int languageCode)
    {
      this.crmServerUrl = crmServerUrl;
      this.organizationName = organizationName;
      this.languageCode = languageCode;
      config = new AppSettingsReader();
    }

    private void CreateCrmService()
    {
      if (crm != null)
        return;
      crm = new CrmService();
      crm.Url = crmServerUrl + "/mscrmservices/2007/crmservice.asmx";
      crm.UseDefaultCredentials = true;
      crm.CrmAuthenticationTokenValue = new CrmAuthenticationToken();
      crm.CrmAuthenticationTokenValue.OrganizationName = organizationName;
    }

    private void CreateMetaDataService()
    {
      if (meta != null)
        return;
      meta = new MetadataService();
      meta.Url = crmServerUrl + "/mscrmservices/2007/metadataservice.asmx";
      meta.UseDefaultCredentials = true;
      meta.CrmAuthenticationTokenValue = new CrmAuthenticationToken();
      meta.CrmAuthenticationTokenValue.OrganizationName = organizationName;      
    }

    /// <summary>
    /// Closes the case with status "Canceled" 
    /// </summary>
    /// <param name="caseId">Case guid</param>
    internal void CancelCase(Guid caseId)
    {
      CreateCrmService();
      SetStateIncidentRequest req = new SetStateIncidentRequest();
      req.IncidentState = IncidentState.Canceled;
      req.IncidentStatus = -1;
      req.EntityId = caseId;
      crm.Execute(req);
    }
    
    /// <summary>
    /// Closes the case with status "Resolved" and the specified subject and
    /// description for the case resolution activity
    /// </summary>
    /// <param name="caseId">Case guid</param>
    /// <param name="subject">Subject of the case resolution activity</param>
    /// <param name="description">Description of the case resolution activity</param>
    internal void ResolveCase(Guid caseId, string subject, string description)
    {
      CreateCrmService();
      incidentresolution resolution = new incidentresolution();
      resolution.incidentid = new Lookup("incident", caseId);
      resolution.subject = subject;
      resolution.description = description;
      CloseIncidentRequest req = new CloseIncidentRequest();
      req.Status = 5;
      req.IncidentResolution = resolution;
      crm.Execute(req);
    }


    /// <summary>
    /// Returns an array of items representing the different procedure types available
    /// and their corresponding integer codes. Used in createprocedure.aspx
    /// </summary>
    /// <returns></returns>
    internal Collection<EntityInfo> GetProcedureMetadata()
    {
      CreateMetaDataService();
      RetrieveAllEntitiesRequest req = new RetrieveAllEntitiesRequest();
      req.MetadataItems = MetadataItems.EntitiesOnly;
      RetrieveAllEntitiesResponse resp = (RetrieveAllEntitiesResponse)meta.Execute(req);
      Collection<EntityInfo> procedureList = new Collection<EntityInfo>();
      foreach (EntityMetadata emd in resp.CrmMetadata)
        if (emd.SchemaName.StartsWith("csp_", StringComparison.OrdinalIgnoreCase ))
          procedureList.Add(new EntityInfo(emd.DisplayName.UserLocLabel.Label, emd.ObjectTypeCode.Value));
      return procedureList;
    }

    /// <summary>
    /// Retrieves the incident and associated contact xml
    /// Used by proceduredetail.aspx to build the addressable form url
    /// </summary>
    /// <param name="incidentId">Case guid</param>
    /// <returns></returns>
    internal IXPathNavigable FetchIncidentAndContactXml(Guid incidentId)
    {
      CreateCrmService();
      string fetchXml = @"
        <fetch mapping='logical'>
          <entity name='incident'>
            <attribute name='csp_procedureid' />
            <attribute name='csp_proceduretype' />
            <attribute name='customerid' />
            <attribute name='ticketnumber' />
            <attribute name='title' />
            <filter>
              <condition attribute='incidentid' operator='eq' value='{0}' />
            </filter>
            <link-entity name='contact' from='contactid' to='customerid'>
              <attribute name='fullname' />
              <attribute name='address1_city' />
              <attribute name='address1_line1' />
              <attribute name='address1_postalcode' />
              <attribute name='emailaddress1' />
              <attribute name='telephone1' />
              <attribute name='fax' />
             </link-entity>
          </entity>
        </fetch>";
      fetchXml = String.Format(CultureInfo.InvariantCulture, fetchXml, incidentId);
      string resultXml = (string)crm.Fetch(fetchXml);
      XmlDocument resultDom = new XmlDocument();
      resultDom.LoadXml(resultXml);
      return resultDom;
    }

    /// <summary>
    /// Retrieves contact information. Used in createprocedure.aspx to fill in the case with contact information
    /// </summary>
    /// <param name="contactId">Contact guid</param>
    /// <returns></returns>
    internal IXPathNavigable FetchContactXml(Guid contactId)
    {
      CreateCrmService();
      string fetchXml = @"
        <fetch mapping='logical'>
          <entity name='contact'>
            <attribute name='fullname' />
            <attribute name='address1_city' />
            <attribute name='address1_line1' />
            <attribute name='address1_postalcode' />
            <attribute name='emailaddress1' />
            <attribute name='telephone1' />
            <attribute name='fax' />
            <filter>
              <condition attribute='contactid' operator='eq' value='{0}' />
            </filter>
          </entity>
        </fetch>";
      fetchXml = String.Format(CultureInfo.InvariantCulture, fetchXml, contactId);
      string resultXml = (string)crm.Fetch(fetchXml);
      XmlDocument resultDom = new XmlDocument();
      resultDom.LoadXml(resultXml);
      return resultDom;
    }

    /// <summary>
    /// Retrieves case and contact information for the VirtualEarth case distribution window
    /// </summary>
    /// <param name="caseIds">Array of case guids</param>
    /// <returns></returns>
    internal string GetCaseCollectionInfo(string[] caseIds)
    {
      CreateCrmService();
      string fetchXml = @"
        <fetch mapping='logical' count='250' distinct='true'>
          <entity name='incident'>
            <attribute name='csp_proceduretype' />
            <attribute name='customersatisfactioncode' />
            <attribute name='incidentid' />
            <attribute name='ticketnumber' />
            <order attribute='customersatisfactioncode' />
            <filter type='or'>
              <condition attribute='incidentid' operator='in'>
              {0}
              </condition>
            </filter>
            <link-entity name='contact' from='contactid' to='contactid'>
              <attribute name='fullname' />
              <attribute name='address1_latitude' />
              <attribute name='address1_longitude' /> 
              <attribute name='telephone1' />
              <attribute name='emailaddress1' />
              <attribute name='address1_line1' />
              <attribute name='address1_city' />
              <attribute name='address1_postalcode' />             
            </link-entity>
          </entity>
        </fetch>";

      StringBuilder sb = new StringBuilder();
      foreach (string caseId in caseIds)
        sb.AppendFormat(CultureInfo.InvariantCulture, "<value>{0}</value>\r\n", caseId);
      fetchXml = String.Format(CultureInfo.InvariantCulture, fetchXml, sb.ToString());
      return crm.Fetch(fetchXml);
    }

    //internal bool IsActive(Guid caseId)
    //{
    //  ColumnSet
    //  crm.Retrieve(EntityName.incident.ToString(), caseId, cs);
    //}


    /// <summary>
    /// Reads a localized label from the web.config file. Returns the standard language label
    /// if the current language code is not found
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public string GetConfigText(string key)
    {
      try
      {
        string localizedKey = String.Format(CultureInfo.InvariantCulture, "{0}_{1}", key, languageCode);
        return (string)config.GetValue(localizedKey, typeof(string));
      }
      catch (InvalidOperationException)
      {
        return (string)config.GetValue(key, typeof(string));
      }
    }


    #region IDisposable Members


    private bool disposed;

    protected virtual void Dispose(bool disposing)
    {
      if (!disposed)
      {
        if (disposing)
        {
          if (crm != null)
            crm.Dispose();
          if (meta != null)
            meta.Dispose();
        }
        disposed = true;
      }
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }



    #endregion
  }
}
